# coding=utf-8
'''
Created on Jun 17, 2009

@license: License: LGPL
@copyright: (c) 2009, dogsing.cn
@author: ant-man
'''

from mocCollection import mocDict, mocList
from mocConstant import mocFalse, mocTrue, mocGNone, mocInt, mocFloat, mocString
from mocInstance import new_instance, MocInstance
from mocObject import MocObject, MocRuntimeException

class MocCallableHasSealed(MocRuntimeException): pass

class MocCannotMarshal(MocRuntimeException):pass

class MocNavtiveCallError(MocRuntimeException):pass


class CallableValue(object):

    def __init__(self, params, code_list):
        # params is a dictionary of name -> (name, code), 
        # for we may deal with parameter has default value
        self.params = params
        self.code_list = code_list
        self.__sealed = False
        self.__sealed_ctx = {}

    @property
    def param_count(self):
        return len(self.params)

    @property
    def sealed(self):
        """
        @return: if current callable is sealed
        """
        return self.__sealed

    def update_sealed_ctx(self, ctx):
        """
        In MOC each callable like function or a closure is a normal instance like other, 
        but when generate a new callable we must specify its context, this method is used 
        for assigning current context to a new  callable.
        
        @param ctx:  context been assigned
        """
        if self.sealed :
            raise MocCallableHasSealed()
        sealed = {}
        self.__sealed_ctx.update(ctx)
        self.__sealed = True

    @property
    def sealed_ctx (self):
        return self.__sealed_ctx

    def __str__(self):
        return "MocCallable instance of %d parameters" % self.param_count


class MocNativeCall(object):

    def __init__(self, func, name=None):
        assert callable(func)
        self.native = func
        self.name = name if name else func.__name__

    def invoke(self, params):
        params = self.marshal(params)
        try :
            ret = self.native(*params)
        except Exception, why:
            raise MocNavtiveCallError(str(why))
        return self.unmarshal(ret)

    def marshal(self, params):
        """
        TODO:
        """
        ret = []
        for param in params :
            if isinstance(param, MocInstance) :
                ret.append(param.pyval())
            else :
                ret.append(param)
        return tuple(ret)

    def unmarshal(self, src):
        """
        TODO:
        """
        if isinstance(src, MocInstance):
            return src
        elif isinstance(src, dict) :
            ret = {}
            for key, val in src :
                ret[self.unmarshal(key)] = self.unmarshal(val)
            return mocDict.new_inst(ret)
        elif isinstance(src, (list, tuple)):
            ret = [self.unmarshal(val) for val in ret]
            return mocList.new_inst(ret)
        elif isinstance(src, int):
            return mocInt.new_inst(src)
        elif isinstance(src, float):
            return mocFloat(src)
        elif isinstance(src, bool):
            return mocTrue if src else mocFalse
        elif isinstance(src, str):
            return mocString.new_inst(src)
        else:
            return mocGNone

    def __str__(self):
        return "Native callable: %s" % self.name


class MocMocNativeCall(MocNativeCall):

    def invoke(self, params):
#        assert isinstance(params, MocInstance)
        try :
            ret = self.native(*params)
        except Exception, why:
            raise MocNavtiveCallError(str(why))
        return self.unmarshal(ret)

    def __str__(self):
        return "Build-in callable: %s" % self.name


class MocCallable(MocObject):
    '''
    classdocs
    '''

    TYPE = "CALLABLE"

    def new_inst(self, value):
        """
        @param value: a tuple of parameters and code list
        """
        if isinstance(value, MocNativeCall) :
            return new_instance(self, value)
        elif isinstance(value, tuple):
            return new_instance(self, CallableValue(*value))

    def as_bool(self, inst):
        return True

    def pyval(self, inst):
        return "__moc__callable__.__%d" % (inst.id)


mocCallable = MocCallable()

# register object's fields

__list_fields = {
    "has_next":mocCallable.new_inst(MocMocNativeCall(mocList.has_next)).id,
    "next":mocCallable.new_inst(MocMocNativeCall(mocList.next)).id,
    "begin_itr": mocCallable.new_inst(MocMocNativeCall(mocList.begin_itr)).id,
    "slice": mocCallable.new_inst(MocMocNativeCall(mocList.slice)).id,
    "append": mocCallable.new_inst(MocMocNativeCall(mocList.add_item)).id,
}
mocList.add_object_field(__list_fields)


__string_fields = {
    "len":mocCallable.new_inst(MocMocNativeCall(mocString.str_len)).id,
}
mocString.add_object_field(__string_fields)




